package net.takela.common.webflux.security.filter;


import net.takela.common.spring.exception.SignErrorException;
import net.takela.common.webflux.security.RequestParamSignProperties;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

public class AppRequestParamSignAuthFilter implements WebFilter, Ordered, ReactiveRequestParamSignFilter {
    private Logger logger = LoggerFactory.getLogger(AppRequestParamSignAuthFilter.class);
    private final RequestParamSignProperties requestParamSignProperties;
    /**
     * 
     */
    public AppRequestParamSignAuthFilter(RequestParamSignProperties requestParamSignProperties) {
        this.requestParamSignProperties = requestParamSignProperties;
    }

    
    /**
     * 
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        if (!requestParamSignProperties.getEnabled()) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();

        AntPathMatcher antPathMatcher = new AntPathMatcher();
        boolean isUncheck = requestParamSignProperties.getUncheckUrls().stream().anyMatch(m -> antPathMatcher.match(m, exchange.getRequest().getPath().value()));
        if (isUncheck) {
            return chain.filter(exchange);
        }

        boolean f = requestParamSignProperties.getCheckUrls().stream().anyMatch(m -> antPathMatcher.match(m, exchange.getRequest().getPath().value()));
        if (!f) {
            return chain.filter(exchange);
        }
        String t = request.getHeaders().getFirst(requestParamSignProperties.getTimestampHeader());
        String sign = request.getHeaders().getFirst(requestParamSignProperties.getSignHeader());
        if (StringUtils.isEmpty(t) || StringUtils.isEmpty(sign)) {
//            return Mono.empty();
//            return Mono.error(new AccessDeniedException(null, null, "miss some header"));
            return Mono.error(new SignErrorException("miss some header"));
        }

        String q = request.getURI().getRawQuery();
        String bodyStr = null;
        //非GET从body取数据
        if (request.getMethod() != HttpMethod.GET) {
            Flux<DataBuffer> body = request.getBody();
            AtomicReference<String> bodyRef = new AtomicReference<>();
            body.subscribe(buffer -> {
                CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                DataBufferUtils.release(buffer);
                bodyRef.set(charBuffer.toString());
            });
            bodyStr = bodyRef.get();
        }

        String s2 = requestParamSignProperties.getKey() + (StringUtils.isEmpty(q) ? "" : q) + (StringUtils.isEmpty(bodyStr) ? "" : bodyStr) + t;
        String sign2 = DigestUtils.sha256Hex(s2);
        logger.debug("sign check|timestamp:{}|sign:{}|origin:{}|sign2:{}", t, sign, s2, sign2);
        if (sign.equalsIgnoreCase(sign2)) {
            return chain.filter(exchange);
        }
        return Mono.error(new SignErrorException("miss some header"));
    }

    
    /**
     * 
     */
    @Override
    public int getOrder() {
        return -99999;
    }
}
